package com.ahy231.service.impl;

import com.ahy231.entity.*;
import com.ahy231.mapper.*;
import com.ahy231.utils.*;
import com.ahy231.dto.*;
import com.ahy231.service.*;
import com.alibaba.fastjson.JSON;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import lombok.extern.slf4j.Slf4j;
import org.apache.shiro.SecurityUtils;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Service;
import org.springframework.util.Assert;
import org.springframework.web.client.RestTemplate;

import java.util.*;
import java.util.concurrent.TimeUnit;
import java.util.stream.Collectors;

/**
 * @author ahy231
 * @date 2021/7/23 14:13
 * @description
 */
@Service
@Slf4j
public class UserServiceImpl implements UserService {

    @Value("${wechat.appId}")
    private String appId;
    @Value("${wechat.secret}")
    private String secret;
    @Value("${wechat.oauth2Url}")
    private String oauth2Url;

    private final LoginService loginService;

    private final UserMapper userMapper;
    private final UserRoleMapper userRoleMapper;
    private final LoginMapper loginMapper;
    private final RoleMapper roleMapper;

    private final RedisTemplate<String, Object> redisTemplate;
    private final RestTemplate restTemplate;
    private final JWTUtil jwtUtil;

    public UserServiceImpl(LoginService loginService, UserMapper userMapper, UserRoleMapper userRoleMapper, LoginMapper loginMapper, RoleMapper roleMapper, RedisTemplate<String, Object> redisTemplate, RestTemplate restTemplate, JWTUtil jwtUtil) {
        this.loginService = loginService;
        this.userMapper = userMapper;
        this.userRoleMapper = userRoleMapper;
        this.loginMapper = loginMapper;
        this.roleMapper = roleMapper;
        this.redisTemplate = redisTemplate;
        this.restTemplate = restTemplate;
        this.jwtUtil = jwtUtil;
    }

    @Override
    public String wxLogin(String encryptedData, String iv, String code) {
        // 1、向微信服务器 使用登录凭证 code 获取 session_key 和 openid
        Map<String, String> params = new HashMap<>();
        params.put("appId", appId);
        params.put("secret", secret);
        params.put("code", code);
        params.put("grant_type", "authorization_code");
        // 请求 openId 和 session_key
        String response = restTemplate.getForObject(oauth2Url, String.class, params);
        WxLoginInfo wxLoginInfo = JSON.parseObject(response, WxLoginInfo.class);
        Assert.notNull(wxLoginInfo, "获取用户信息出错");
        String session_key = wxLoginInfo.getSession_key();
        String openId = wxLoginInfo.getOpenid();
        Long userId = userMapper.getUserIdByOpenId(openId);

        //第一次登录
        UserRoles userRoles = new UserRoles();
        if (userId == null) {
            //在数据库中添加该用户
            User user = new User();
            user.setOpenId(openId);
            // 2、对encryptedData加密数据进行AES解密，获取用户的微信信息
            String result = AesUtil.decrypt(encryptedData, session_key, iv, "UTF-8");
            Assert.hasLength(result, "提取 encryptedData 信息错误");
            WxUserInfo wxUserInfo = JSON.parseObject(result, WxUserInfo.class);
            Assert.notNull(wxUserInfo, "无法提取用户信息");
            user.setUsername(wxUserInfo.getNickName());
            user.setSex(wxUserInfo.getSex());
            user.setImgUrl(wxUserInfo.getHeadimgurl());
            userId = addDefaultUser(userRoles, user);
        } else {
            userRoles.setUserId(userId);
            userRoles.setRoles(loginMapper.getRolesByUserId(userId));
        }
        //向redis保存用户信息
        redisTemplate.opsForSet().add(userRoles.getUserId() + "", userRoles.getRoles().toArray(new String[0]));
        redisTemplate.expire(userRoles.getUserId() + "", 1000 * 60 * 60 * 5, TimeUnit.MILLISECONDS); // 设置过期时间为 5 小时
        //生成token并返回
        return jwtUtil.createToken(userId);
    }

    @Override
    public Boolean addRole(String role) {
        Long userId = getUserId();
        Assert.notNull(userId, "service 获取不到用户 id");
        Set<String> rolesByUserId = loginService.getRolesByUserId(userId);
        Assert.isTrue(!rolesByUserId.contains(role), "用户已经拥有该角色");
        Role selectOne = roleMapper.selectOne(new QueryWrapper<Role>().eq("role", role));
        Assert.notNull(selectOne, "找不到这种角色");
        redisTemplate.opsForSet().add(userId + "", role);
        redisTemplate.expire(userId + "", 1000 * 60 * 60 * 5, TimeUnit.MILLISECONDS); // 设置过期时间为 5 小时
        return userRoleMapper.insert(new UserRole(null, userId, selectOne.getRoleId())) == 1;
    }

    @Override
    public Boolean removeRole(String role) {
        if (role.equals("admin") || role.equals("user")) return false;
        Long userId = getUserId();
        Assert.notNull(userId, "service 获取不到用户 id");
        Role selectOne = roleMapper.selectOne(new QueryWrapper<Role>().eq("role", role));
        Assert.notNull(selectOne, "找不到这种角色");
        redisTemplate.opsForSet().remove(userId + "", role);
        redisTemplate.expire(userId + "", 1000 * 60 * 60 * 5, TimeUnit.MILLISECONDS); // 设置过期时间为 5 小时
        return userRoleMapper.delete(new QueryWrapper<UserRole>().eq("user_id", userId).eq("role_id", selectOne.getRoleId())) > 0;
    }

    @Override
    public Boolean modifyUserInfo(UserInfo userInfo) {
        Long userId = getUserId();
        Assert.notNull(userId, "service 获取不到用户 id");
        if (userInfo.getRoles() != null) {
            Set<String> infoRoles = userInfo.getRoles();
            List<String> toRemove = getRoles();
            toRemove.removeAll(infoRoles);
            for (String s : toRemove) {
                removeRole(s);
            }
            List<String> userRoles = getRoles();
            infoRoles.removeAll(new HashSet<>(userRoles));
            for (String infoRole : infoRoles) {
                addRole(infoRole);
            }
        }
        return userMapper.updateById(new User(
                userId,
                userInfo.getUsername(),
                userInfo.getPhone(),
                userInfo.getEmail(),
                userInfo.getSex(),
                userInfo.getTrueName(),
                userInfo.getIdCard(),
                null,
                userInfo.getImgUrl()
        )) > 0;
    }

    @Override
    public UserInfo getUserInfo(Long userId) {
        if (userId == null) {
            Long selfId = getUserId();
            Assert.notNull(selfId, "service 获取不到用户 id");
            userId = selfId;
        }
        User user = userMapper.selectById(userId);
        Set<String> roles = loginService.getRolesByUserId(userId);
        return new UserInfo(user.getUserId(), user.getUsername(), user.getPhone(), user.getEmail(), user.getSex(), user.getTrueName(), user.getIdCard(), user.getImgUrl(), roles);
    }

    @Override
    public List<String> getRoles() {
        Long userId = getUserId();
        Assert.notNull(userId, "service 获取不到用户 id");
        return loginMapper.getRolesByUserId(userId);
    }

    @Override
    public String testLogin() {
        return jwtUtil.createToken(0L);
    }

    private long addDefaultUser(UserRoles userRoles, User user) {
        // user
        userMapper.insert(user);
        Long userId = user.getUserId();
        Assert.notNull(userId, "默认用户插入数据库失败");
        // role
        userRoleMapper.addUserRole(userId, "user");
        userRoles.setUserId(userId);
        userRoles.getRoles().add("user");
        return userId;
    }

    private Long getUserId() {
        return JWTUtil.getUserId(SecurityUtils.getSubject().getPrincipal().toString());
    }
}
